package main

import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"unsafe"
)

var mtx sync.RWMutex

var gconf *Config

var gconf2 unsafe.Pointer

//////////////////////////////////////

func GetConf() *Config {
	mtx.RLock()
	defer mtx.RUnlock()
	return gconf
}

func SetConf(conf *Config) {
	mtx.Lock()
	defer mtx.Unlock()
	gconf = conf
}

//////////////////////////////////////

func GetConf2() *Config {
	return (*Config)(atomic.LoadPointer(&gconf2))
}

func SetConf2(conf *Config) {
	atomic.StorePointer(&gconf2, unsafe.Pointer(conf))
}

//////////////////////////////////////

type Config struct {
	V int
	M map[int]int
}

var wait = sync.WaitGroup{}

func Reload() {
	wait.Done()

	nconf := new(Config)
	nconf.V = rand.Int()
	nconf.M = make(map[int]int)
	nconf.M[rand.Int()] = rand.Int()

	// gconf = conf // race here
	//SetConf(nconf)
	SetConf2(nconf)
}

func main() {
	N := 1
	wait.Add(N + 1)

	Reload()

	//fmt.Println(gconf.M)
	//fmt.Println(GetConf().M)
	fmt.Println(GetConf2().M)

	for i := 0; i < N; i++ {
		go Reload()
	}

	wait.Wait()
	for i := 0; i < 5; i++ {
		//fmt.Println(gconf.M) // race here
		//fmt.Println(GetConf().M)
		fmt.Println(GetConf2().M)
	}

	// map map
	{
		c := &Chatroom{}
		wait := &sync.WaitGroup{}

		// Join r1
		for i := 0; i < 3; i++ {
			wait.Add(1)
			go func(i int) {
				defer wait.Done()
				for j := 0; j < 1000; j++ {
					c.Join([]uint64{1}, uint64(i))
				}
			}(i)
		}

		// Join r2
		for i := 0; i < 3; i++ {
			wait.Add(1)
			go func(i int) {
				defer wait.Done()
				for j := 0; j < 1000; j++ {
					c.Join([]uint64{2}, uint64(i))
				}
			}(i)
		}

		// Quit r1
		for i := 0; i < 3; i++ {
			wait.Add(1)
			go func(i int) {
				defer wait.Done()
				for j := 0; j < 1000; j++ {
					c.Quit([]uint64{1}, uint64(i))
				}
			}(i)
		}

		// Quit r2
		for i := 0; i < 3; i++ {
			wait.Add(1)
			go func(i int) {
				defer wait.Done()
				for j := 0; j < 1000; j++ {
					c.Quit([]uint64{2}, uint64(i))
				}
			}(i)
		}

		// Range r1
		for i := 0; i < 3; i++ {
			wait.Add(1)
			go func(i int) {
				defer wait.Done()
				for j := 0; j < 1000; j++ {
					c.Range(1, func(ID uint64) {})
				}
			}(i)
		}

		// Range r2
		for i := 0; i < 3; i++ {
			wait.Add(1)
			go func(i int) {
				defer wait.Done()
				for j := 0; j < 1000; j++ {
					c.Range(2, func(ID uint64) {})
				}
			}(i)
		}

		wait.Wait()

		fmt.Println("Done")
	}
}
